/*================================================================
*   Copyright (C) 2023 ZMKJ Ltd. All rights reserved.
*   
*   Filename   ：main.c
*   Creator    ：Chase Wu
*   Create Date：2023/11/30
*   Description：
*
================================================================*/
#if !defined(LOG_TAG)
    #define LOG_TAG                    "main"
#endif
#undef LOG_LVL
#if defined(MAIN_LOG_LVL)
    #define LOG_LVL                    MAIN_LOG_LVL
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <getopt.h>
#include <execinfo.h>

#include "elog.h"
#include "ccu.h"

#include "version.h"

struct cmdline_input_param{
	uint8_t debug_level;
	uint8_t file_ctrl;
	char config_file[256];
	char log_path[256];
	char filter_tag[128];
};

static char log_filename[512] = {0};
static int log_index = 0;

static struct option longOpts[] = {
	{ "config",    required_argument, NULL, 'c' },
	{ "debug",     no_argument,       NULL, 'd' },
	{ "debug mods",required_argument, NULL, 'f' },
	{ "help",      no_argument,       NULL, 'h' },
	{ "type",      required_argument, NULL, 't' },
	{ "version",   no_argument,       NULL, 'v' },
	{ 0, 0, 0, 0 }
};

static void print_version(void)
{
	printf("Build GCC Version:%s \n", __VERSION__);
	printf("Build Date:%s \n", __DATE__);
	printf("Build Time:%s \n", __TIME__);
	printf("\n");
}

static void print_app_version(void)
{
	int ret = 0;
	char  str_version_tmp[128] = {'\0'};

	sprintf(str_version_tmp, "V%d.%d.%d_%04d%02d%02d %02d:%02d:%02d", APP_MAJOR_VERSION, APP_MINOR_VERSION,  APP_PATCH_VERSION, \
		    APP_BUILD_YEAR, APP_BUILD_MONTH, APP_BUILD_DAY,APP_BUILD_HOUR,APP_BUILD_MIN,APP_BUILD_SEC);
	printf("\e[1;31mAPPVER: %s\033[0m \r\n", str_version_tmp);

	FILE *fd;
	char filename[128] = {0};

	ret = system("rm -rf /software/*_V*");
	sprintf(filename, "/software/V%d.%d.%d_%04d%02d%02d_%02d%02d%02d", 
				      APP_MAJOR_VERSION, 
					  APP_MINOR_VERSION,  APP_PATCH_VERSION, APP_BUILD_YEAR, 
					  APP_BUILD_MONTH, APP_BUILD_DAY, APP_BUILD_HOUR, 
					  APP_BUILD_MIN, APP_BUILD_SEC);
	fd = fopen(filename, "w");
	if(fd > 0){
		fclose(fd);
	}
}

static void print_usage(char *app)
{
	fprintf(stdout, "Usage:  %s <-d> <debug level> \n", app);
	fprintf(stdout, "        -c, --config      Set config file \n");
	fprintf(stdout, "        -d, --debug mods  Choose mods to debug \n");
	fprintf(stdout, "        -h, --help        Display this help \n");
	fprintf(stdout, "        -l, --log_level   set log filter level \n");
	fprintf(stdout, "        -t, --type        Set log file path,default current directory \n");
	fprintf(stdout, "        -v, --version     Display this version \n");
	fprintf(stdout, "\n");
}

int log_init(char *file, uint8_t level, uint8_t file_ctrl, char *filter_tag)
{
	int ret = 0;

	/* close printf buffer */
	setbuf(stdout, NULL);

	if (file_ctrl)
	{
		if (file) 
		{
			int temp_fd;
			char dir[128];
			char *tmp;
			char s_log_file[128] = { 0 };

			strcpy(dir, file);
			strncpy(s_log_file, file, sizeof(s_log_file));
			
			if ((tmp = strrchr(dir, '/')) != NULL) {
				tmp[0] = 0;
				create_path(dir);
			}
		}
	}
	/* initialize EasyLogger */
	//printf("set easylogger level = %u\n", level);
	elog_init(file, level, file_ctrl, filter_tag);
	// printf("string filter_tag len = %d, contain = %s\n", strlen(filter_tag), filter_tag);

	return ret;
}

int log_exit(void)
{
	elog_stop();
	elog_deinit();
	return 0;
}

static int read_log_index(char *path)
{
	int ret = 0;
	uint32_t index = 0;
	char index_file[512] = {0};
	
	sprintf(index_file, "%s/.index", path);

	if((access(index_file, F_OK)) == -1){
		FILE *fd;

		fd = fopen(index_file, "w");
		if(fd > 0){
			ret = fwrite(&index, 1, sizeof(index), fd);
			fclose(fd);
		}
	}else{
		FILE *fd;

		fd = fopen(index_file, "rw+");
		if(fd > 0){
			ret = fread(&index, 1, sizeof(index), fd);
			index += 1;
			fseek(fd, 0, SEEK_SET);
			// log_i("index=%d", index);
			ret = fwrite(&index, 1, sizeof(index), fd);
			fclose(fd);
		}
	}

	return index;
}

static int cmdline_parse(int argc, char* argv[], struct cmdline_input_param *pcmd_param)
{
	int cmd;
	int opt_index = 0;

	while((cmd = getopt_long(argc, argv, "c:d:l:t:hv", longOpts, &opt_index)) != -1){
		switch (cmd) {
			case 0:{
				printf("option %s set %s", longOpts[opt_index].name, optarg);
			}break;
			case 'c':{
				memcpy(pcmd_param->config_file, optarg, strlen(optarg));
				// printf("%s\n", config_file);
			}break;
			case 'd':{
				// pcmd_param->debug_level = ELOG_LVL_VERBOSE;
				memcpy(pcmd_param->filter_tag, optarg, strlen(optarg));
				printf("you specified debug mods %s\n", pcmd_param->filter_tag);
			}break;
			case 'h':{
				print_usage(argv[0]);
				return 0;
			}break;
			case 'l':{
				pcmd_param->debug_level = atoi(optarg);
				printf("cmdline_parse set elog level = %u\n", pcmd_param->debug_level);
			}break;
			case 't':{
				struct timeval tv;
				struct tm result;
				char excu_app_name[32] = {0};
				char *papp_name = "app_eo";

				// pcmd_param->debug_level = ELOG_LVL_INFO;
				pcmd_param->file_ctrl   = 1;
				memcpy(pcmd_param->log_path, optarg, strlen(optarg));
				// printf("-------------------------------------------------%s\n", log_path);
				if((access(pcmd_param->log_path, 0)) == -1){                           
					mkdir(pcmd_param->log_path, S_IRWXU);           
				}

				gettimeofday(&tv, NULL);
				localtime_r(&tv.tv_sec, &result);
				log_index = read_log_index(pcmd_param->log_path);
				strcpy(excu_app_name, argv[0]);
				// papp_name = strstr(excu_app_name, "./");
				// (papp_name != NULL)
				// {
					// papp_name += strlen("./");
					// printf("-------------main.c find app_name = %s--------------\n", papp_name);
				// }
				sprintf(log_filename, "%s/%d-%s_%04d-%02d-%02d_%02d%02d%02d.log", 
								pcmd_param->log_path,
								log_index,
								papp_name,
								result.tm_year + 1900,
								result.tm_mon + 1,
								result.tm_mday,
								result.tm_hour,
								result.tm_min,
								result.tm_sec);
			}break;
			case 'v':{
				print_version();
				return 0;
			}break;
			
			default:{}break;
		}
	}

	log_init(log_filename, pcmd_param->debug_level, pcmd_param->file_ctrl, pcmd_param->filter_tag);
	return 1;
}

static bool main_running_flag = true;
static void main_show_stack(void)
{
	void *stack_buffer[16];

	int n = backtrace(stack_buffer, 16);
	char **symbols = backtrace_symbols(stack_buffer, n);
	if(!symbols){
		perror("backtrace symbols\n");
		exit(-1);
	}
	for(int i = 0; i < n; i++){
		printf("%s%s", symbols[i], (i % 2) ? "\n" : " ");
	}
	printf("\n");
	free(symbols);
}

static void main_handle_sig(int32_t signo)
{
    if(signo == SIGINT || signo == SIGTERM) {
        main_running_flag = false;
    }else if(signo == SIGSEGV) {
		log_e("Segmentation fault");
		main_show_stack();
        exit(0);
    }
    log_i("App exit signle!");
}

static void main_signal_init(void)
{
    signal(SIGINT, main_handle_sig);
    signal(SIGTERM, main_handle_sig);
	signal(SIGSEGV, main_handle_sig);
	signal(SIGPIPE, SIG_IGN);
}

int main(int argc, char* argv[])
{
	int32_t ret;
	struct ccu* pccu;
	struct uconfig *puconfig;
	struct cmdline_input_param cmd_param = {0};

	// main_signal_init();

	print_app_version();

	cmd_param.debug_level = ELOG_LVL_VERBOSE;
	cmd_param.file_ctrl   = 0;
	sprintf(cmd_param.log_path, "%s", "/software");
	sprintf(cmd_param.config_file, "%s", "/software/config.json");

	ret = cmdline_parse(argc, argv, &cmd_param);
	if(ret == 0){
		log_i("Application Exit");
		return 0;
	}

	ret = create_init_ccu(&pccu);
	if(ret < 0){
		log_e("create init ccu error");
		return ret;
	}
	
    puconfig = pccu->puconfig;
    puconfig->input_config_file   = cmd_param.config_file;
	puconfig->default_config_file = cmd_param.config_file;
    puconfig->op->load(puconfig);
    // puconfig->op->show(puconfig);
	ret = pccu->op->start(pccu);
	if(ret < 0){
		log_e("ccu start error");
		goto main_exit;
	}

	while(main_running_flag){
		pccu->op->handle(pccu);
		// sleep(1);
	}

main_exit:
	pccu->op->stop(pccu);
	release_destroy_ccu(pccu);
	
	log_exit();

	return 0;
}
